package com.h.mybatis.mapper;


import com.h.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.*;

import java.util.List;

// 这个 @Mapper 是ibatis的注解，也就是Mybatis的注解，让其交给Spring管理
@Mapper
public interface UserInfoMapper {

    // 对于接口实现，有两种方式
    // 1. 注解的方式 是其中之一
    // 2. XML的方式

    // 查询注解 @Select
    // 对于查询来说，我们的 Java的对象需要和MySQL中的属性名称一一对应，如果没有对应就会为null
    // 比如这个一开始的这个 Java中的对象属性为 deleteFlag而MySQL为delete_flag，这就没有对应上，所以查询就会为null
    // 那么如何解决这个问题呢？有三种方法

    // @Select 这个注解后面() 中的代码，就是其对应的接口所实现的代码
    @Select("select * from user_info")
    List<UserInfo> selectAll();
    // 对于测试这个成不成功，可以使用 单元测试的方法，就是test这个包下的
    // 还可以使用接口连接到浏览器中进行测试

    // 方法1：使用别名（as）的方式
    @Select("select id,username,`password`,age,gender,phone,delete_flag as deleteFlag," +
            "create_time as createTime,update_time as updateTime from user_info")
    // 这样那些 Java对象中的属性和MySQL属性不一样的就可以对应上了
    List<UserInfo> selectAll2();

    // 方法2：使用 @Results 和 @Result注解，映射的方法
    // 这个还有一个搭配注解 @ResultMap 来配合，这时候我们的 @Results 这个注解就要添加 id和value
    // 可以使别的地方也可以使用这个映射关系
    @Results(id = "BeanMap", value = {
            @Result(column = "delete_flag",property = "deleteFlag"),
            @Result(column = "create_time",property = "createTime"),
            @Result(column = "update_time",property = "updateTime")
    })
    @Select("select * from user_info")
    List<UserInfo> selectAll3();

    // 方法3：使用 配置文件 来进行配置，会自动的把MySQL中的 如delete_flag的这个属性名转换为deleteFlag
    // 这样之后我们的查询就可以全部查询了

    // 传递参数，在增删查改中都可以传递
    // 我们使用 #{参数名} 来进行接收参数

    // 单个参数
    // 由前端进行传递
    @ResultMap(value = "BeanMap")
    @Select("select * from user_info where id = #{id}")
    // 这里的参数接收 可以使用 List(0~n个) 或者 UserInfo(0~1个)这个对象都可以，因为只会返回一个
    List<UserInfo> selectAllById(Integer id);

    @Select("select * from user_info where id = #{id}")
    UserInfo selectAllById2(Integer id);

    // 因为这里 只有一个参数，所以这里{} 中的参数名称是可以所以改变的，因为只有一个参数
    @Select("select * from user_info where id = #{abc}")
    UserInfo selectAllById3(Integer id);

    // 多个参数
    @Select("select * from user_info where username = #{username} and password = #{password}")
    List<UserInfo> selectNameAndPassword(String username,String password);
    // 对于上面的传递的参数，我们可以更改其顺序，同样不会出现错误，同时在传入参数多的时候，我们还会桉顺序生成 param1，param2....
    // 但是我们一般不使用这个 param1 这个类自动生成的代表顺序的属性，因为 不好读，还容易错误
    @Select("select * from user_info where username = #{username} and password = #{password}")
    List<UserInfo> selectNameAndPassword2(String password,String username);

    @Select("select * from user_info where username = #{param2} and password = #{param1}")
    List<UserInfo> selectNameAndPassword3(String password,String username);

    // 当我们想要更改我们的参数名的话，我们需要使用 @Param 注解，但是更改后的参数要和 #{} 中的参数一致
    @Select("select * from user_info where username = #{userName} and password = #{passWord}")
    List<UserInfo> selectNameAndPassword4(@Param("userName") String username,@Param("passWord") String password);

    // 传递对象，mybatis会根究Java属性自动生成参数，我们 #{} 这里的直接使用 属性名称即可
    @Select("select * from user_info where username = #{username} and password = #{password}")
    List<UserInfo> selectNameAndPassword5(UserInfo userInfo);

    // 增加注解 @Insert

    // 传递对象
    @Insert("insert into user_info (username,`password`,age) values (#{username},#{password},#{age})")
    // 返回的是影响的行数
    Integer insertUser(UserInfo userInfo);

    // 如果我们想要的到我们添加操作，所影响的是第几行，也就是 主键 的话，我们需要使用一个注解来设置 —— @Options
    // useGeneratedKeys 这个参数默认为false，我们更改成true，并且设置 keyProperty 这个参数
    // 这个时候我们就可以 使用 对象.getId() 这个方法得到 id 的值
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into user_info (username,`password`,age) values (#{username},#{password},#{age})")
    // 返回的是影响的行数
    Integer insertUser2(UserInfo userInfo);

    // 我们对于 对象也可以使用 @Param 这个注解来更改参数名称，但是需要今夕你给特殊处理
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into user_info (username,`password`,age) values (#{userinfo.username}," +
            "#{userinfo.password},#{userinfo.age})")
    // 返回的是影响的行数
    Integer insertUser3(@Param("userinfo") UserInfo userInfo);

    // 对于这个 @Options 这个注解来说，如果参数不是对象的话，是一些属性，那么就拿不到 自增的id是多少
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into user_info (username,`password`,age) values (#{username},#{password},#{age})")
    Integer insertUser4(String username,String password,Integer age);

    // 删除 注解@Delete()
    @Delete("delete from user_info where id = #{id}")
    Integer delete(Integer id);

    // 更新 注解 @Update()
    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Update("update user_info set delete_flag = #{deleteFlag},phone = #{phone} where id = #{id}")
    Integer update(UserInfo userInfo);


    // #{} 和 ${} 的区别
    // 1、#{} 是预处理SQL，${} 是即时SQL直接替换
    @Select("select * from user_info where id = ${id}")
    // 所以这里不会往数据库中传入参数，会直接把参数添加到 SQL语句中一起传输
    List<UserInfo> selectAllById4(Integer id);

    // 2、#{} 接收参数会根据类型进行修饰，比如：字符串会添加 '' ，但是 ${} 不会，会直接拼接，不会走任何处理
    // 这样直接传入就会报错，因为MySQL语句中字符串使用添加''或者""的 这里是直接拼接，就会报错，所以我们手动添加''或者""
//    @Select("select * from user_info where username = ${username} and password = ${password}")
    @Select("select * from user_info where username = '${username}' and password = '${password}'")
    List<UserInfo> selectNameAndPassword6(String password,String username);

    // 3、最重要的问题，${} 会存在SQL注入 问题，因为是直接拼接的，当输入如：' or 1='1 这个字符串的时候，会直接拼接，导致出现全部打印的问题
    // 来试试看，根据前端进行传输信息
    // 我们也可以根据代码来解决这个 SQL注入问题
    @Select("select * from user_info where username = '${username}' and password = '${password}'")
    List<UserInfo> selectNameAndPassword7(String username,String password);


    // 但是 虽然 ${} 存在SQL注入问题，但是有一些情况我们不需要使用 ${} 这个方式
    // 比如：排序查询，我们会输入desc或asc，这时候如果使用#{}注入的话，因为输入的是String类型，会自动添加''或""
    // 这个时候desc或asc就不是一个关键字了，就是字符串了，所以我们需要 直接拼接的性质，使用${}
    @Select("select * from user_info order by id ${order}")
    List<UserInfo> selectUserInfoByOrder(String order);

    // like查询
    // 正常我们使用 #{} 来查询就会报错，虽然使用 ${} 可以使其查询出来，但是会存在SQL注入的问题
    @Select("select * from user_info where username like '%${userName}%' ")
    List<UserInfo> selectUserInfoByLike(String userName);
    // 这里存在SQL注入的问题，所以我们还是需要使用 #{} 这个注入
    // 这个时候我们需要借助 MySQL的一个关键字 —— concat，这个可以使多个字符串进行拼接
    @Select("select * from user_info where username like concat('%',#{username},'$')")
    List<UserInfo> selectUserInfoByLike2(String username);

    // 注解下的 动态SQL
    // 这种不是很推荐的方式
    @Insert("<script>" +
            "insert into user_info " +
            "<trim prefixOverrides=\",\" suffixOverrides=\",\" prefix=\"(\" suffix=\")\">" +
            "<if test='username!=null'> username, </if>" +
            "<if test='password!=null'> password, </if>" +
            "<if test='age!=null'> age, </if>" +
            "<if test='gender!=null'> gender, </if>" +
            "<if test='phone!=null'> phone, </if>" +
            "</trim> VALUES " +
            "<trim prefixOverrides=\",\" suffixOverrides=\",\" prefix=\"(\" suffix=\")\">" +
            "<if test='username!=null'> #{username}, </if>" +
            "<if test='password!=null'> #{password}, </if>" +
            "<if test='age!=null'> #{age}, </if>" +
            "<if test='gender!=null'> #{gender}, </if>" +
            "<if test='phone!=null'> #{phone}, </if>" +
            "</trim>" +
            "</script>")
    Integer insertUser7(UserInfo userInfo);
}
